﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using SISCON.Dao;
using SISCON.Model;
using SISCON.Common;
using log4net;
using SISCON.Vo;

namespace SISCON.Bl
{
    
    /// <summary>
    ///  Pasa al dao corespondiente los datos de usuarios
    /// </summary>
    public class UserBL
    {
        private static ILog _Logger = LogManager.GetLogger(typeof(UserBL));

        private SessionBL _SessionBL = new SessionBL();

        private UserDAO _UserDAO = new UserDAO();
        
        /// <summary>
        /// Pasa al dao correspondiente los datos para autentificar al usuario
        /// </summary>
        /// <param name="loginName">nombre de usuario</param>
        /// <param name="password">contrasenia</param>
        /// <returns>Llave de autentificacion</returns>        
        public AuthToken Authenticate(string loginName, string password)
        {
            try
            {
                User user = _UserDAO.Authenticate(loginName, password);
                if (user != null)
                {
                    if (!user.IsStatic && !user.IsActive)
                    {
                        throw new SISCONException("Este usuario fue desactivado. Favor de contactar al administrador del sistema.");
                    }

                    string SessionUUID = _SessionBL.CreateSession(user);

                    AuthToken authToken = new AuthToken(user.DisplayName, SessionUUID);
                    User currentUser = GetFromSessionUUID(authToken.SessionUUID);

                    authToken.RoleId = currentUser.RoleId.ToString();
                    authToken.UserId = currentUser.UserId.ToString();
                    authToken.Permissions = currentUser.Permissions;

                    return authToken;
                }
                else
                {
                    throw new SISCONException("No fue posible acceder al sistema. Favor de intentar más tarde.");
                }
            }
            catch (Exception ex)
            {
                if (!(ex is SISCONException))
                {
                    _Logger.Error("Error in Authenticate()", ex);
                }
                throw ex;
            }
        }
        
        /// <summary>
        /// Pasa al dao correspondiente la session del usuairo
        /// </summary>
        /// <param name="SessionUUID">Session del usuario</param>
        /// <returns>Un usuario que tiene session </returns>       
        public User GetFromSessionUUID(string SessionUUID)
        {
            try
            {
                return _UserDAO.GetFromSessionUUID(SessionUUID);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        /// <summary>
        /// Manda al dao correspondiente los datos de session 
        /// </summary>
        /// <param name="SessionUUID">Session del usuario</param>
        /// <param name="RequiredAccessLevel">Nivel de acceso que quiere acceder</param>
        /// <param name="RequiredRoles">Los roles requeridos</param>
        /// <returns>El id del usuario</returns>        
        public long RequireRole(string SessionUUID, string RequiredAccessLevel, params string[] RequiredRoles)
        {
            try
            {
                Session session = _SessionBL.GetSession(SessionUUID);

                //Revisar si tiene acceso al recurso con el nivel de acceso solicitado
                if (!HasAccessToResource(SessionUUID, RequiredAccessLevel, RequiredRoles))
                {
                    throw new SISCONException("No tiene acceso a este recurso.");
                }
            }
            catch (Exception exp)
            {
                throw exp;
            }

            return GetFromSessionUUID(SessionUUID).UserId;
        }

        /// <summary>
        /// Pasa los parametros de session , nivel de acceso y roles equeridos al dao correspondiente
        /// </summary>
        /// <param name="SessionUUID">Session del usuario</param>
        /// <param name="RequiredAccessLevel">Nivel de acceso</param>
        /// <param name="RequiredRoles">Roles requeridos</param>
        /// <returns>Verdadero si puede accesar Falso caso contrario</returns>        
        public bool HasAccessToResource(string SessionUUID, string RequiredAccessLevel, params string[] RequiredRoles)
        {
            bool UserHasAccess = _UserDAO.HasAccessToResource(SessionUUID, RequiredAccessLevel, RequiredRoles);
            if (UserHasAccess)
            {
                _SessionBL.UpdateSession(SessionUUID);
            }
            return UserHasAccess;
        }

        /// <summary>
        /// Pasa los parametros de session al dao correspondiente
        /// </summary>
        /// <param name="sessionUUID">Session del usuario</param>
        /// <returns>Lista de usuarios</returns>
        public List<User> GetAllUsers(string sessionUUID)
        {
            try
            {
                RequireRole(sessionUUID, Role.READ_ACCESS, Role.USERS);
                return _UserDAO.GetAllUsers();
            }
            catch (Exception ex)
            {
                if (!(ex is SISCONException))
                {
                    _Logger.Error("Error in GetAllUsers()", ex);
                }
                throw ex;
            }
        }

        /// <summary>
        /// Pasa los parametros al doa correspondiente para guardar o actualizar un usuario
        /// </summary>
        /// <param name="sessionUUID">Session del usuario</param>
        /// <param name="modifiedUserId">Id modificado del usuario</param>
        /// <param name="loginName">Nombre de usuario</param>
        /// <param name="displayName">Nombre a desplegar</param>
        /// <param name="password">Constrasenia</param>
        /// <param name="email">Mail del usuario</param>
        /// <param name="roleId">El id del rol del usuario</param>
        /// <param name="enabled">Si estara disponible</param>
        public void SaveUser(string sessionUUID, string modifiedUserId, string loginName, string displayName, string password, string email,
                             string roleId, bool enabled)
        {
            try
            {
                long userId = RequireRole(sessionUUID, Role.WRITE_ACCESS, Role.USERS);
                _UserDAO.SaveUser(modifiedUserId, loginName, displayName, password, email, roleId, enabled);
            }
            catch (Exception ex)
            {
                if (!(ex is SISCONException))
                {
                    _Logger.Error("Error in SaveUser()", ex);
                }
                throw ex;
            }
        }

        /// <summary>
        /// Pasa los parametros al dao correspondiente para borrar a un usuario
        /// </summary>
        /// <param name="sessionUUID">Session del usuario</param>
        /// <param name="userId">El id del Usuario</param>
        public void DeleteUser(string sessionUUID, string userId)
        {
            try
            {
                RequireRole(sessionUUID, Role.WRITE_ACCESS, Role.USERS);
                _UserDAO.DeleteUser(userId);
            }
            catch (Exception ex)
            {
                if (!(ex is SISCONException))
                {
                    _Logger.Error("Error in DeleteUser()", ex);
                }
                throw ex;
            }
        }
        
        /// <summary>
        /// Pasa los parametros al dao correspondiente para cerrar la session de un usuario
        /// </summary>
        /// <param name="sessionUUID">Session del usuario</param>
        public void Logout(string sessionUUID)
        {
            try
            {
                _UserDAO.Logout(sessionUUID);
            }
            catch (Exception ex)
            {
                if (!(ex is SISCONException))
                {
                    _Logger.Error("Error in Logout()", ex);
                }
                throw ex;
            }

        }
    }
}